Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: patch removeEventListener to properly remove patched callbacks #158

Merged
merged 12 commits into from
Jul 24, 2020
Merged

fix: patch removeEventListener to properly remove patched callbacks #158

merged 12 commits into from
Jul 24, 2020

Conversation

johnbley
Copy link
Member

@johnbley johnbley commented Jul 23, 2020

We need to patch removeEventListener in order to compensate for wrapping callbacks passed to addEventListener.

@johnbley johnbley requested a review from a team July 23, 2020 12:39
@codecov
Copy link

codecov bot commented Jul 23, 2020

Codecov Report

Merging #158 into master will increase coverage by 0.04%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##           master     #158      +/-   ##
==========================================
+ Coverage   94.06%   94.10%   +0.04%     
==========================================
  Files          74       74              
  Lines        3536     3582      +46     
  Branches      374      387      +13     
==========================================
+ Hits         3326     3371      +45     
- Misses        210      211       +1     
Impacted Files Coverage Δ
...try-plugin-user-interaction/src/userInteraction.ts 93.80% <0.00%> (+0.94%) ⬆️

@dyladan
Copy link
Member

dyladan commented Jul 23, 2020

@rezakrimi the lint action is failing completely. Can you please look into this?

@rezakrimi
Copy link
Contributor

@rezakrimi the lint action is failing completely. Can you please look into this?

Sure.

@obecny
Copy link
Member

obecny commented Jul 23, 2020

Can you please provide more details what is the exact problem here ?, thx

@johnbley
Copy link
Member Author

Can you please provide more details what is the exact problem here ?, thx

Sure thing! The added test shows that the addEventListener instrumentation actually breaks apps because it doesn't have the compensating logic for removeEventListener. A patched callback is actually added as the event listener, and when the app tries to remove the original one, it's not found/ignored, leaving our patched callback present (along with unexpected calls to the underlying original one). For some apps, this doesn't matter, but for others this can be arbitrarily bad (breaking functionality, thrown errors, infinite loops, etc. etc.).

@obecny
Copy link
Member

obecny commented Jul 23, 2020

it looks fine, the failing build might be connected with yesterday eslint merge, we have to check it @dyladan https://circleci.com/gh/open-telemetry/opentelemetry-js-contrib/1546
EDIT:
I might be wrong with the link, but when checking the lint in this PR something else is failing not related to this PR

@dyladan
Copy link
Member

dyladan commented Jul 23, 2020

it looks fine, the failing build might be connected with yesterday eslint merge, we have to check it @dyladan circleci.com/gh/open-telemetry/opentelemetry-js-contrib/1546
EDIT:
I might be wrong with the link, but when checking the lint in this PR something else is failing not related to this PR

It is definitely related. eslint isn't running at all because of a gts dependency error.

@rezakrimi
Copy link
Contributor

it looks fine, the failing build might be connected with yesterday eslint merge, we have to check it @dyladan circleci.com/gh/open-telemetry/opentelemetry-js-contrib/1546
EDIT:
I might be wrong with the link, but when checking the lint in this PR something else is failing not related to this PR

It is definitely related. eslint isn't running at all because of a gts dependency error.

yes the caching was wrong, I'll make a PR today to fix it

@rezakrimi
Copy link
Contributor

#159 should fix the lint check.

fix: fixing caching issue for lint action
@dyladan
Copy link
Member

dyladan commented Jul 23, 2020

@johnbley if you update from master now the lint should run properly

@johnbley
Copy link
Member Author

@johnbley if you update from master now the lint should run properly

Will do, but please hold on merging until I find the time to properly handle some edge cases better.

@johnbley johnbley marked this pull request as draft July 23, 2020 17:56
@johnbley
Copy link
Member Author

Looks like the lint step in the build is failing again, but this time with out-of-date code... I ran lint:fix and pushed those changes.

@johnbley johnbley marked this pull request as ready for review July 23, 2020 18:58
@rezakrimi
Copy link
Contributor

Looks like the lint step in the build is failing again, but this time with out-of-date code... I ran lint:fix and pushed those changes.

That's strange. Same thing happened to my PR on the main opentelemetry-js repo. For me it just got resolved after pushing another commit.

const listener = function () {
called = true;
};
// add same listener three different ways
Copy link
Member

@obecny obecny Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about one scenario. To check that can you modify this a bit to test the case with overwriting the previous event with the same callback

let called2 = 0;
const listener2 = function () {
  called2++;
};
document.body.addEventListener('bodyEvent', listener2);
document.body.addEventListener('bodyEvent', listener2);
document.body.addEventListener('bodyEvent', listener2);

// now fun
document.body.dispatchEvent(new Event('bodyEvent'));
assert.strictEqual(called2, 1);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed my example

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good catch and an easy fix for this one based on the tracking state. Pushing fix momentarily.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is one more thing a bonus :). Currently you can call addEventListener providing a 3rd param options. In options you can set once:true which means the event will be removed automatically. It might be worth to test that too.

Copy link
Member

@obecny obecny Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let called2 = 0;
const listener2 = function () {
  called2++;
};
document.body.addEventListener('bodyEvent', listener2);
document.body.addEventListener('bodyEvent', listener2, { once: true });

// now fun
document.body.dispatchEvent(new Event('bodyEvent'));
assert.strictEqual(called2, 1);
document.body.dispatchEvent(new Event('bodyEvent'));
assert.strictEqual(called2, 1); // error ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and you can revert it add once first and then add without options, heh small things and many edge cases

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh. There's also the case that once:true shouldn't prevent us from allowing it to be adding later (it's not really a double-register). Will dive in and fix this one tomorrow. Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx for taking this into deep details :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't leave it alone; handled the once:true callbacks properly being removed when an event is dispatched.

Copy link
Member

@obecny obecny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great job, thx for investigating this further :)

Copy link
Member

@dyladan dyladan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from minor suggestions by @obecny this LGTM

): boolean {
let listener2Type = this._wrappedListeners.get(listener);
if (!listener2Type) {
listener2Type = new Map();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to be coerced into a WeakMap when you set it on _wrappedListeners?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, though making the HTMLElement reference weak is a good idea. Unfortunately I don't see a way to do it since there are no iterators/size/isEmpty methods available on WeakMap and without it we'd definitely leak. I'll ponder this but I'd like to get this fix in as it is since it's clearly better than the current breaking of functional behavior.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks for the clarification!

@johnbley
Copy link
Member Author

great job, thx for investigating this further :)

Thanks to all for spotting further issues and edge cases!

@obecny obecny merged commit 4e551ad into open-telemetry:master Jul 24, 2020
@dyladan dyladan added the bug Something isn't working label Aug 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants